package com.cloudfun.campusshare.service;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cloudfun.campusshare.common.constant.Code;
import com.cloudfun.campusshare.common.constant.IdentityType;
import com.cloudfun.campusshare.common.constant.WechatMsgType;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.dto.UnifiedOrderDTO;
import com.cloudfun.campusshare.common.model.dto.WeChatCode2SessionRespDTO;
import com.cloudfun.campusshare.common.model.dto.WeChatWithoutCodePayDTO;
import com.cloudfun.campusshare.common.model.dto.WechatMessageDTO;
import com.cloudfun.campusshare.common.model.vo.OrderFinishMessageVO;
import com.cloudfun.campusshare.common.model.vo.UserSubscribeMessageVO;
import com.cloudfun.campusshare.configuration.property.WeChatProperties;
import com.cloudfun.campusshare.entity.UserAuthEntity;
import com.cloudfun.campusshare.util.XmlUtil;
import com.cloudfun.campusshare.util.tencent.wechat.WeChatUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class WeChatService {
    private final static String WE_CHART_TOKEN = "wechat_token";

    private final static String WE_CHART_TOKEN_LOCK = "WE_CHART_TOKEN_LOCK";

    private static final String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={appId}&secret={secret}&js_code={jsCode}&grant_type=authorization_code";

    /**
     * 统一下单
     */
    private static final String UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    private static final String PAY_WITHOUT_CODE = "https://api.mch.weixin.qq.com/pay/pappayapply";

    private static final String GET_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={secret}";

    private static final String SEND_MEG = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN";

    /**
     * 申请退款
     */
    private static final String REFUND = "https://api.mch.weixin.qq.com/secapi/pay/refund";


    /**
     * 公众号用户授权
     */
    private static final String OFFICIAL_USER_AUTH = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appId}&secret={secret}&code={jsCode}&grant_type=authorization_code";


    @Autowired
    private WeChatProperties weChatProperties;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${wechat.apiKey}")
    private String apiKey;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private UserAuthService userAuthService;

    @Value("${wechat.message.subscribe.template.id}")
    private String subscribeMsgTemplateId;

    @Value("${wechat.message.order.finish.template.id}")
    private String orderFinishMsgTemplateId;

    @Value("${wechat.official.appId}")
    private String weChatOfficialAppId;

    @Value("${wechat.official.secret}")
    private String weChatOfficialSecret;
    @PostConstruct
    public void setup() {
        restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
        converterList.set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        for (HttpMessageConverter<?> httpMessageConverter : converterList) {
            if (httpMessageConverter instanceof AbstractHttpMessageConverter) {
                AbstractHttpMessageConverter<?> abstractHttpMessageConverter = (AbstractHttpMessageConverter) httpMessageConverter;
                List<MediaType> supportedMediaTypes = new ArrayList<>(abstractHttpMessageConverter.getSupportedMediaTypes());
                // 拿到JsonConverter
                if (supportedMediaTypes.contains(MediaType.APPLICATION_JSON)) {
                    supportedMediaTypes.add(0, MediaType.TEXT_PLAIN);
                    // 让其能处理text/plain的数据
                    ((AbstractHttpMessageConverter<?>) httpMessageConverter).setSupportedMediaTypes(supportedMediaTypes);
                }
            }
        }
    }

    public WeChatCode2SessionRespDTO jsCode2SessionInfo(String jsCode) {
        Map<String, String> params = new HashMap<>(4);
        params.put("appId", weChatProperties.getAppId());
        params.put("secret", weChatProperties.getSecret());
        params.put("jsCode", jsCode);

        return restTemplate.getForObject(JSCODE_TO_SESSION_URL, WeChatCode2SessionRespDTO.class, params);
    }


    public SortedMap unifiedOrder(UnifiedOrderDTO unifiedOrderDTO) {
        SortedMap<String, Object> map = WeChatUtil.req2Map(unifiedOrderDTO, true);
        String sign = WeChatUtil.sign(map, apiKey);
        unifiedOrderDTO.setSign(sign);
        String param = XmlUtil.writeValueAsString(unifiedOrderDTO).replace("UnifiedOrderDTO", "xml");

        String result = restTemplate.postForObject(UNIFIED_ORDER, param, String.class);
        log.info("微信支付输入参数:{},返回结果:{}", JSON.toJSONString(unifiedOrderDTO), result);
        SortedMap<String, Object> re = XmlUtil.readValue(result);
        if (!"SUCCESS".equals(re.get("return_code"))) {
            log.info("通信失败,输入参数：{},返回结果:{}", JSON.toJSONString(unifiedOrderDTO), result);
            throw new BusinessException(Code.ERROR, "生成微信预付单失败");
        }
        if (!"SUCCESS".equals(re.get("result_code"))) {
            log.info("生成微信预付单失败,输入参数：{},返回结果:{}", JSON.toJSONString(unifiedOrderDTO), result);
            throw new BusinessException(Code.ERROR, "生成微信预付单失败");
        }
        if (!WeChatUtil.verifySign(re, apiKey)) {
            throw new BusinessException(Code.ERROR, "验签失败！");
        }

        //调起支付签名字段计算

        return genSign4Re((String) re.get("prepay_id"));
    }

    private SortedMap<String, Object> genSign4Re(String prepayId) {
        SortedMap<String, Object> signMap = new TreeMap<>();
        signMap.put("appId", weChatProperties.getAppId());
        signMap.put("timeStamp", Instant.now().getEpochSecond());
        signMap.put("nonceStr", WeChatUtil.genRandomStr());
        signMap.put("package", "prepay_id=" + prepayId);
        signMap.put("signType", "MD5");
        String sign = WeChatUtil.sign(signMap, weChatProperties.getApiKey());
        signMap.put("paySign", sign);
        return signMap;
    }

    public boolean delagePay(WeChatWithoutCodePayDTO weChatWithoutCodePayDTO) {
        SortedMap<String, Object> map = WeChatUtil.req2Map(weChatWithoutCodePayDTO, true);
        String sign = WeChatUtil.sign(map, apiKey);
        weChatWithoutCodePayDTO.setSign(sign);
        String param = XmlUtil.writeValueAsString(weChatWithoutCodePayDTO).replace("WeChatWithoutCodePayDTO", "xml");
        String result = restTemplate.postForObject(PAY_WITHOUT_CODE, param, String.class);
        SortedMap<String, Object> re = XmlUtil.readValue(result);
        if (!WeChatUtil.verifySign(re, apiKey)) {
            throw new BusinessException(Code.ERROR, "验签失败！");
        }


        if (!"SUCCESS".equals(re.get("return_code")) || !"SUCCESS".equals(re.get("result_code"))) {
            log.info("生成微信预付单失败,输入参数：{},返回结果:{}", JSON.toJSONString(weChatWithoutCodePayDTO), result);
            throw new BusinessException(Code.ERROR, "代扣款失败");
        }
        return true;
    }

    /**
     * 向用户发送订阅信息
     *
     * @param userId
     */
    public void sendMessageToUser(String userId, WechatMsgType msgType,Map<String,String> params) {
        WechatMessageDTO wechatMessageDTO = new WechatMessageDTO();
        UserAuthEntity auth = userAuthService.getByUserIdAndType(userId, IdentityType.WE_CHAT);
        wechatMessageDTO.setTouser(auth.getIdentifier());
        switch (msgType){
            case USER_SUBSCRIBE_REMIND:
                UserSubscribeMessageVO userSubscribeMessageVO = new UserSubscribeMessageVO();
                Map<String,String> washPoint = new HashMap<>();
                washPoint.put("value",params.get("washPointName"));
                Map<String,String> deviceName = new HashMap<>();
                deviceName.put("value",params.get("deviceName").length() > 20?params.get("deviceName").substring(0,20):params.get("deviceName"));
                Map<String,String> tip = new HashMap<>();
                tip.put("value","预约的设备已经轮到你了，尽快去开启哦");

                userSubscribeMessageVO.setThing1(washPoint);
                userSubscribeMessageVO.setThing2(deviceName);
                userSubscribeMessageVO.setThing3(tip);
                wechatMessageDTO.setTemplate_id(subscribeMsgTemplateId);
                wechatMessageDTO.setData(userSubscribeMessageVO);
                break;
            case ORDER_COMPLETE_REMIND:
                OrderFinishMessageVO orderFinishMessageVO = new OrderFinishMessageVO();
                Map<String,String> washPoint1 = new HashMap<>();
                washPoint1.put("value",params.get("washPointName"));
                Map<String,String> deviceName1 = new HashMap<>();
                deviceName1.put("value",params.get("deviceName").length() > 20?params.get("deviceName").substring(0,20):params.get("deviceName"));
                Map<String,String> tip1 = new HashMap<>();
                tip1.put("value","您的租用订单已完成");

                orderFinishMessageVO.setThing6(washPoint1);
                orderFinishMessageVO.setThing7(deviceName1);
                orderFinishMessageVO.setThing5(tip1);
                wechatMessageDTO.setTemplate_id(orderFinishMsgTemplateId);
                wechatMessageDTO.setData(orderFinishMessageVO);
                break;
        }

        wechatMessageDTO.setMiniprogram_state("developer");
        System.out.println(JSON.toJSONString(wechatMessageDTO));
        String result = restTemplate.postForObject(SEND_MEG.replace("ACCESS_TOKEN", getWechatToken()), JSON.toJSONString(wechatMessageDTO), String.class);
        log.info("调用微信向用户发送订阅信息接口,结果->{}", result);
    }

    public String getWechatToken() {
        String token = stringRedisTemplate.opsForValue().get(WE_CHART_TOKEN);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }

        Map<String, String> param = new HashMap<>();
        param.put("grant_type", "client_credential");
        param.put("appId", weChatProperties.getAppId());
        param.put("secret", weChatProperties.getSecret());
        String result = restTemplate.getForObject(GET_TOKEN, String.class, param);
        JSONObject jre = JSON.parseObject(result);
        if (jre.getInteger("errcode") != null) {
            log.info("获取微信access_token异常，错误代码：{},错误信息：{}", jre.getInteger("errcode"), jre.getString("errmsg"));
            throw new BusinessException(Code.ERROR, "获取微信token异常");
        } else {
            String access_token = jre.getString("access_token");
            Integer expireTime = jre.getInteger("expires_in");
            stringRedisTemplate.opsForValue().set(WE_CHART_TOKEN, access_token, expireTime, TimeUnit.SECONDS);
            return access_token;
        }


    }

    /**
     * 公众号获取openId
     * @param jsCode
     * @return
     */
    public String getOpenId4OfficialAccount(String jsCode){
        Map<String, String> param = new HashMap<>();
        param.put("appId", weChatOfficialAppId);
        param.put("secret", weChatOfficialSecret);
        param.put("jsCode",jsCode);

        String result = restTemplate.getForObject(OFFICIAL_USER_AUTH, String.class, param);

        JSONObject jsonObject = JSON.parseObject(result);
        if (jsonObject.getInteger("errcode") != null) {
            log.info("获取微信公众号用户openid异常，错误代码：{},错误信息：{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
            throw new BusinessException(Code.ERROR, "获取微信公众号用户openid异常");
        } else {
            log.info("获取微信公众号用户openId成功，结果：{}",result);
            String openid = jsonObject.getString("openid");
            return openid;
        }
    }

//    public static void main(String[] args) throws UnsupportedEncodingException {
//        UnifiedOrderDTO unifiedOrderDTO = new UnifiedOrderDTO();
//        unifiedOrderDTO.setAppId("wx2a459d4bb51bc4a7");
//
//        unifiedOrderDTO.setMchId("1589334181");
//        unifiedOrderDTO.setNonceStr(WeChatUtil.genRandomStr());
////        unifiedOrderDTO.setBody(orderEntity.getOrderName());
//        unifiedOrderDTO.setBody("test");
//        unifiedOrderDTO.setOutTradeNo("OD202004280942270001");
//        unifiedOrderDTO.setTotalFee(100);
//
////        unifiedOrderDTO.setSpbillCreateIp(getIp(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()));
//        unifiedOrderDTO.setSpbillCreateIp("172.17.1.108");
//
//        unifiedOrderDTO.setNotifyUrl("Internal.gan-dao.com/v1/wechat/notify/payment");
//
//        unifiedOrderDTO.setOpenid("oviY5433BujbjCVYrGBlLGPHnU1g");
//        SortedMap<String, Object> map = WeChatUtil.req2Map(unifiedOrderDTO, true);
//        String sign = WeChatUtil.sign(map, "gandaozhinenggandaozhineng202000");
//        unifiedOrderDTO.setSign(sign);
//        System.out.println(XmlUtil.writeValueAsString(unifiedOrderDTO).replace("UnifiedOrderDTO", "xml"));
//        SortedMap<String, Object> stringObjectSortedMap = XmlUtil.readValue("<xml><return_code><![CDATA[SUCCESS]]></return_code>\n" +
//                "<return_msg><![CDATA[OK]]></return_msg>\n" +
//                "<appid><![CDATA[wx2a459d4bb51bc4a7]]></appid>\n" +
//                "<mch_id><![CDATA[1589334181]]></mch_id>\n" +
//                "<nonce_str><![CDATA[hNz292565KyVUHxl]]></nonce_str>\n" +
//                "<sign><![CDATA[A5F4E8FCB51EDC07C4301E640954A1E9]]></sign>\n" +
//                "<result_code><![CDATA[SUCCESS]]></result_code>\n" +
//                "<prepay_id><![CDATA[wx29102716433394576cc26d811176393700]]></prepay_id>\n" +
//                "<trade_type><![CDATA[JSAPI]]></trade_type>\n" +
//                "</xml>");
//        System.out.println(stringObjectSortedMap.get("prepay_id"));
//        System.out.println(WeChatUtil.verifySign(stringObjectSortedMap, "gandaozhinenggandaozhineng202000"));
//    }

}
